1 module backtraced; 2 version(Windows) version = UseBacktraced; 3 else version(Android) version = UseNullBacktraced; 4 else version(linux) version = UseBacktraced; 5 else version = UseNullBacktraced; 6 7 8 9 version(UseNullBacktraced) 10 void backtraced_Register() { } 11 12 version(UseBacktraced): 13 import core.demangle; 14 15 version (Windows) 16 { 17 18 pragma(lib, "dbghelp.lib"); 19 import core.sys.windows.windef; 20 import core.sys.windows.imagehlp; 21 import core.sys.windows.winbase; 22 import core.sys.windows.dbghelp; 23 import core.stdc.stdlib : free, calloc; 24 import core.stdc.stdio : fprintf, stderr; 25 import core.stdc.string : memcpy, strncmp, strlen; 26 27 struct SYMBOL_INFO 28 { 29 ULONG SizeOfStruct; 30 ULONG TypeIndex; 31 ULONG64[2] Reserved; 32 ULONG Index; 33 ULONG Size; 34 ULONG64 ModBase; 35 ULONG Flags; 36 ULONG64 Value; 37 ULONG64 Address; 38 ULONG Register; 39 ULONG Scope; 40 ULONG Tag; 41 ULONG NameLen; 42 ULONG MaxNameLen; 43 CHAR[1] Name; 44 } 45 46 extern (Windows) USHORT RtlCaptureStackBackTrace(ULONG FramesToSkip, ULONG FramesToCapture, PVOID* BackTrace, PULONG BackTraceHash); 47 extern (Windows) BOOL SymFromAddr(HANDLE hProcess, DWORD64 Address, PDWORD64 Displacement, SYMBOL_INFO* Symbol); 48 extern (Windows) BOOL SymGetLineFromAddr64(HANDLE hProcess, DWORD64 dwAddr, PDWORD pdwDisplacement, IMAGEHLP_LINEA64* line); 49 50 version(DigitalMars) 51 void printStackTrace() 52 { 53 import hip.util.string; 54 enum MAX_DEPTH = 256; 55 void*[MAX_DEPTH] stack; 56 57 HANDLE process = GetCurrentProcess(); 58 ushort frames = RtlCaptureStackBackTrace(0, MAX_DEPTH, stack.ptr, null); 59 SYMBOL_INFO* symbol = cast(SYMBOL_INFO*) calloc((SYMBOL_INFO.sizeof) + 256, 1); 60 symbol.MaxNameLen = 255; 61 symbol.SizeOfStruct = SYMBOL_INFO.sizeof; 62 63 IMAGEHLP_LINEA64 line = void; 64 line.SizeOfStruct = SYMBOL_INFO.sizeof; 65 66 DWORD dwDisplacement; 67 68 static int ends_with(const(char)* str, const(char)* suffix) 69 { 70 if (!str || !suffix) 71 return 0; 72 size_t lenstr = strlen(str); 73 size_t lensuffix = strlen(suffix); 74 if (lensuffix > lenstr) 75 return 0; 76 return strncmp(str + lenstr - lensuffix, suffix, lensuffix) == 0; 77 } 78 79 for (uint i = 0; i < frames; i++) 80 { 81 SymFromAddr(process, cast(DWORD64)(stack[i]), null, symbol); 82 SymGetLineFromAddr64(process, cast(DWORD64)(stack[i]), &dwDisplacement, &line); 83 84 // auto f = frames - i - 1; 85 char[] funcName = demangle(symbol.Name.ptr[0..symbol.NameLen]); 86 auto fname = line.FileName; 87 auto lnum = line.LineNumber; 88 89 if (ends_with(fname, __FILE__)) 90 continue; // skip trace from this module 91 if(funcName.indexOf("_d_run_main2") != -1) 92 break; 93 94 fprintf(stderr, "%s:%i - %.*s\n", fname, lnum, cast(int)funcName.length, funcName.ptr); 95 } 96 97 free(symbol); 98 } 99 extern (Windows) LONG TopLevelExceptionHandler(PEXCEPTION_POINTERS pExceptionInfo) 100 { 101 import hip.util.system; 102 enum NullPointerMessage = 0xC0000005; 103 import hip.util.conv; 104 105 string msg; 106 switch(pExceptionInfo.ExceptionRecord.ExceptionCode) 107 { 108 case NullPointerMessage: 109 msg = "Caught NullPointerException (0xC0000005)"; 110 break; 111 default: 112 msg = "Caught Exception (0x"~toHex(pExceptionInfo.ExceptionRecord.ExceptionCode)~")"; 113 break; 114 } 115 116 version(DigitalMars) ///DMD can't print the stack trace. 117 { 118 fprintf(stderr, "%.*s\n", cast(int)msg.length, msg.ptr); 119 printStackTrace(); 120 } 121 else 122 throw new Exception(msg); 123 return EXCEPTION_CONTINUE_SEARCH; 124 } 125 126 void backtraced_Register() 127 { 128 SymInitialize(GetCurrentProcess(), null, true); 129 SymSetOptions(SYMOPT_LOAD_LINES | SYMOPT_DEBUG); 130 SetUnhandledExceptionFilter(&TopLevelExceptionHandler); 131 } 132 } 133 134 version (linux) 135 { 136 import core.stdc.signal : SIGSEGV, SIGFPE, SIGILL, SIGABRT, signal; 137 import core.stdc.stdlib : free, exit; 138 import core.stdc.string : strlen, memcpy; 139 import core.stdc.stdio : fprintf, stderr, sprintf, fgets, fclose, FILE; 140 import core.sys.posix.unistd : STDERR_FILENO, readlink; 141 import core.sys.posix.signal : SIGUSR1; 142 import core.sys.posix.stdio : popen, pclose; 143 import core.sys.linux.execinfo : backtrace, backtrace_symbols; 144 import core.sys.linux.dlfcn : dladdr, dladdr1, Dl_info, RTLD_DL_LINKMAP; 145 import core.sys.linux.link : link_map; 146 import core.demangle : demangle; 147 148 void backtraced_Register() 149 { 150 signal(SIGSEGV, &handler); 151 signal(SIGUSR1, &handler); 152 } 153 154 // TODO: clean this mess 155 // TODO: use core.demangle instead 156 extern (C) void handler(int sig) nothrow @nogc 157 { 158 enum MAX_DEPTH = 32; 159 160 string signal_string; 161 switch (sig) 162 { 163 case SIGSEGV: 164 signal_string = "SIGSEGV"; 165 break; 166 case SIGFPE: 167 signal_string = "SIGFPE"; 168 break; 169 case SIGILL: 170 signal_string = "SIGILL"; 171 break; 172 case SIGABRT: 173 signal_string = "SIGABRT"; 174 break; 175 default: 176 signal_string = "unknown"; 177 break; 178 } 179 180 fprintf(stderr, "-------------------------------------------------------------------+\r\n"); 181 fprintf(stderr, "Received signal '%s' (%d)\r\n", signal_string.ptr, sig); 182 fprintf(stderr, "-------------------------------------------------------------------+\r\n"); 183 184 void*[MAX_DEPTH] trace; 185 int stack_depth = backtrace(&trace[0], MAX_DEPTH); 186 char** strings = backtrace_symbols(&trace[0], stack_depth); 187 188 enum BUF_SIZE = 1024; 189 char[BUF_SIZE] syscom = 0; 190 char[BUF_SIZE] my_exe = 0; 191 char[BUF_SIZE] output = 0; 192 193 readlink("/proc/self/exe", &my_exe[0], BUF_SIZE); 194 195 fprintf(stderr, "executable: %s\n", &my_exe[0]); 196 fprintf(stderr, "backtrace: %i\n", stack_depth); 197 198 for (auto i = 2; i < stack_depth; ++i) 199 { 200 auto line = strings[i]; 201 auto len = strlen(line); 202 bool insideParenthesis; 203 int startParenthesis; 204 int endParenthesis; 205 for (int j = 0; j < len; j++) 206 { 207 // () 208 if (!insideParenthesis && line[j] == '(') 209 { 210 insideParenthesis = true; 211 startParenthesis = j + 1; 212 } 213 else if (insideParenthesis && line[j] == ')') 214 { 215 insideParenthesis = false; 216 endParenthesis = j; 217 } 218 } 219 auto addr = convert_to_vma(cast(size_t) trace[i]); 220 FILE* fp; 221 222 auto locLen = sprintf(&syscom[0], "addr2line -e %s %p | ddemangle", &my_exe[0], addr); 223 fp = popen(&syscom[0], "r"); 224 225 auto loc = fgets(&output[0], output.length, fp); 226 fclose(fp); 227 228 // printf("loc: %s\n", loc); 229 230 auto getLen = strlen(output.ptr); 231 232 char[256] func = 0; 233 memcpy(func.ptr, &line[startParenthesis], (endParenthesis - startParenthesis)); 234 sprintf(&syscom[0], "echo '%s' | ddemangle", func.ptr); 235 fp = popen(&syscom[0], "r"); 236 237 output[getLen - 1] = ' '; // strip new line 238 auto locD = fgets(&output[getLen], cast(int)(output.length - getLen), fp); 239 fclose(fp); 240 241 fprintf(stderr, "%s", output.ptr); 242 } 243 exit(-1); 244 } 245 246 // https://stackoverflow.com/questions/56046062/linux-addr2line-command-returns-0/63856113#63856113 247 size_t convert_to_vma(size_t addr) nothrow @nogc 248 { 249 Dl_info info; 250 link_map* link_map; 251 dladdr1(cast(void*) addr, &info, cast(void**)&link_map, RTLD_DL_LINKMAP); 252 return addr - link_map.l_addr; 253 } 254 }